#pragma once

#include "epoll.hpp"
#include "socket.hpp"
#include "nocopy.hpp"

#include <iostream>
#include <memory>
#include <map>
#include <string>
#include <functional>

using namespace std;
// using std::cout;
// using std::endl;

#define portdefault 8088

class TcpServer :public nocopy
{
public:
    TcpServer(uint16_t port = portdefault)
        :_port(port),
        _listensock_ptr(new Sock),
        _epoll_ptr(new epoller)
    {}
    
    void Init()
    {
        _listensock_ptr->Socket();
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();
        // 初始化(生成)epoll模型
        _epoll_ptr->Init();
    }

    void Accepter(int n = 0)
    {
        string ip;
        uint16_t port;
        int sock = _listensock_ptr->Accept(&ip, &port);

        _epoll_ptr->epoll_status(sock, EPOLL_CTL_ADD, event_in);
    }

    void Recver(int sock)
    {
        char buffer[4098];
        ssize_t n = recv(sock, buffer, sizeof(buffer), 0);
        buffer[n] = 0;
        if(n > 0)
        {
            printf("User: %d Say@ %s", sock, buffer);
        }
        else
        {
            _epoll_ptr->epoll_status(sock, EPOLL_CTL_DEL, event_in);
            lg(Debug, "User: %d Quit", sock);
        }
    }

    void Distribute(int n)
    {
        int listensock = _listensock_ptr->Fd();
        for(int i = 0; i < n; i++)
        {
            if(_ep[i].data.fd != listensock)
            {
                // Recver(_ep[i].data.fd);
                _func["Recver"](_ep[i].data.fd);
            }
            else
            {
                // Accepter();
                _func["Accepter"](0);
            }
            
        }
    }

    // epoll_create epoll_wait epoll_ctl
    // EPOLL_CTL_ADD EPOLL_CTL_MOD EPOLL_CTL_DEL
    // #define event_in EPOLLIN
    // #define event_out EPOLLOUT
    void Start()
    {
        _func["Accepter"] = [=](int n){Accepter();};
        _func["Recver"] = [=](int sock){Recver(sock);};
        int listenfd = _listensock_ptr->Fd();
        // 把套接字文件以只关心读的方式打入红黑树
        _epoll_ptr->epoll_status(listenfd, EPOLL_CTL_ADD, event_in);
        while(true)
        {
            int n = _epoll_ptr->epoll_Take(_ep);
            if(n > 0)
            {
                Distribute(n);
            }
            else if(n == 0 || n < 0){continue;}
        }
        
    }


    ~TcpServer()
    {
        _listensock_ptr->Close();
    }

private:
    struct epoll_event _ep[1024];
    shared_ptr<Sock> _listensock_ptr;
    shared_ptr<epoller> _epoll_ptr;
    map<string, function<void(int fd)>> _func; // 用于指派任务时,将任务以包装的方式派发出去
    uint16_t _port = 8088;
};